import socket
import time
import threading
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox

def get_local_ips():
    """获取本地所有IPv4地址"""
    ips = []
    hostname = socket.gethostname()
    try:
        # 只获取IPv4地址
        for info in socket.getaddrinfo(hostname, None, family=socket.AF_INET):
            ip = info[4][0]
            if ip not in ips and not ip.startswith('127.'):
                ips.append(ip)
    except Exception as e:
        messagebox.showerror("错误", f"获取本地IP地址时出错: {e}")
    return ips if ips else ['127.0.0.1']

class BroadcastApp:
    """GUI应用程序类"""
    def __init__(self, root):
        self.root = root
        self.root.title("UDP广播服务")
        self.root.geometry("800x600")  # 设置窗口默认大小
        
        # 主框架 - 增加内部padding
        self.mainframe = ttk.Frame(root, padding="20 15 20 15")
        self.mainframe.pack(fill=tk.BOTH, expand=True)
        
        # 输入框区域
        input_frame = ttk.Frame(self.mainframe)
        input_frame.pack(fill=tk.X, pady=5)
        
        # 使用grid布局确保对齐
        input_frame.grid_columnconfigure(1, weight=1, uniform="input_col")
        
        # IP选择框 (第一行)
        ttk.Label(input_frame, text="选择IP地址:").grid(row=0, column=0, padx=(0, 10), sticky=tk.W)
        self.ip_var = tk.StringVar()
        self.ip_combobox = ttk.Combobox(input_frame, textvariable=self.ip_var, width=25)
        self.ip_combobox.grid(row=0, column=1, sticky=tk.EW, padx=(0, 10))
        
        # 文件名输入框和按钮 (第二行)
        ttk.Label(input_frame, text="文件名:").grid(row=1, column=0, padx=(0, 10), sticky=tk.W)
        self.filename_var = tk.StringVar(value="LIFT_BL_49P.bin")
        self.filename_entry = ttk.Entry(input_frame, textvariable=self.filename_var, width=25)
        self.filename_entry.grid(row=1, column=1, sticky=tk.EW, padx=(0, 10))
        
        # 按钮放在文件名输入框右侧
        self.start_button = ttk.Button(input_frame, text="开始广播", width=12, command=self.toggle_broadcast)
        self.start_button.grid(row=1, column=2, padx=(10, 0))
        
        self.status_var = tk.StringVar(value="准备就绪")
        status_label = ttk.Label(self.mainframe, textvariable=self.status_var)
        status_label.pack(side=tk.BOTTOM, anchor=tk.W)
        
        # 接收数据显示区域
        recv_frame = ttk.Frame(self.mainframe)
        recv_frame.pack(fill=tk.BOTH, expand=True)
        
        # 接收数据标题和清除按钮
        recv_header = ttk.Frame(recv_frame)
        recv_header.grid_columnconfigure(0, weight=1)
        recv_header.pack(fill=tk.X)
        
        ttk.Label(recv_header, text="接收数据:").grid(row=0, column=0, sticky=tk.W)
        clear_button = ttk.Button(recv_header, text="清除", width=12, command=self.clear_recv_display)
        clear_button.grid(row=0, column=1, padx=(10, 0), sticky=tk.E)
        
        self.recv_text = tk.Text(recv_frame, height=8, state='disabled')
        self.recv_text.pack(fill=tk.BOTH, expand=True)
        
        # 接收数据队列
        self.recv_queue = []
        
        # 获取本地IP并设置下拉框值
        ips = get_local_ips()
        self.ip_combobox['values'] = ips
        if ips:  # 默认选择第一个IP
            self.ip_var.set(ips[0])
        
        # 初始化广播状态
        self.broadcasting = False
        self.sock = None
        self.send_thread = None
        self.recv_thread = None

    def toggle_broadcast(self):
        """切换广播状态"""
        if self.broadcasting:
            self.stop_broadcast()
        else:
            self.start_broadcast()

    def start_broadcast(self):
        """启动广播服务"""
        ip = self.ip_var.get()
        
        try:
            # 验证IP地址格式
            socket.inet_aton(ip)
            self.status_var.set(f"正在启动广播服务，使用IP: {ip}")
            self.broadcasting = True
            self.start_button.config(text="停止广播")
            self.run_broadcast(ip)
        except socket.error:
            messagebox.showerror("错误", "无效的IP地址格式", parent=self.root)

    def run_broadcast(self, ip):
        """运行广播服务"""
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
        self.stop_event = threading.Event()  # 用于通知线程停止
        
        # 绑定IP地址并处理错误
        try:
            self.sock.bind((ip, 1314))
            self.status_var.set(f"广播运行中，使用IP: {ip}")
        except socket.error as e:
            messagebox.showerror("错误", f"IP {ip} 不可用: {e}", parent=self.root)
            self.stop_broadcast()
            return
        
        # 使用Event控制SERVEROK发送
        self.send_serverok_event = threading.Event()
        self.send_serverok_event.set()  # 默认允许发送

        def send_thread():
            while not self.stop_event.is_set():
                try:
                    if self.send_serverok_event.is_set():
                        self.sock.sendto(b'SERVEROK', ('255.255.255.255', 1314))
                    time.sleep(1)  # 1秒间隔
                except Exception as e:
                    if not self.stop_event.is_set():  # 仅处理非主动停止的错误
                        self.root.after(0, lambda: messagebox.showerror("错误", f"发送错误: {e}", parent=self.root))
                    break

        def recv_thread():
            while not self.stop_event.is_set():
                try:
                    data, addr = self.sock.recvfrom(1024)
                    message = data.decode('utf-8')
                    msg = f"收到来自 {addr} 的消息: {message}"
                    print(msg)
                    self.recv_queue.append(msg)
                    
                    if "RequestUpdate" in message:
                        response = f"{ip}&{self.filename_var.get()}"
                        self.sock.sendto(response.encode('utf-8'), ('255.255.255.255', 1314))
                        print(f"已发送响应: {response}")
                    elif "BeigenUpdate" in message:
                        self.send_serverok_event.clear()
                        print("检测到BeigenUpdate，停止发送SERVEROK")
                    elif "EndUpdate" in message:
                        self.send_serverok_event.set()
                        print("检测到EndUpdate，恢复发送SERVEROK")
                except Exception as e:
                    if not self.stop_event.is_set():  # 仅处理非主动停止的错误
                        self.root.after(0, lambda: messagebox.showerror("错误", f"接收错误: {e}", parent=self.root))
                    break
        
        # 启动线程
        self.send_thread = threading.Thread(target=send_thread, daemon=True)
        self.recv_thread = threading.Thread(target=recv_thread, daemon=True)
        self.send_thread.start()
        self.recv_thread.start()
        
        print("UDP广播服务已启动...")

    def stop_broadcast(self):
        """停止广播服务"""
        # 设置停止标志
        if hasattr(self, 'stop_event'):
            self.stop_event.set()
        
        # 先关闭套接字中断recvfrom阻塞
        if self.sock is not None:
            try:
                self.sock.close()
            except Exception as e:
                if "10038" not in str(e):  # 忽略已关闭套接字的错误
                    messagebox.showerror("错误", f"关闭套接字时出错: {e}", parent=self.root)
            finally:
                self.sock = None
        
        # 更新UI状态
        self.broadcasting = False
        self.start_button.config(text="开始广播")
        self.status_var.set("广播已停止")

    def clear_recv_display(self):
        """清除接收显示内容"""
        self.recv_text.configure(state='normal')
        self.recv_text.delete(1.0, tk.END)
        self.recv_text.configure(state='disabled')
        
    def update_recv_display(self):
        """更新接收数据显示"""
        if self.recv_queue:
            message = self.recv_queue.pop(0)
            self.recv_text.configure(state='normal')
            self.recv_text.insert(tk.END, message + "\n")
            self.recv_text.configure(state='disabled')
            self.recv_text.see(tk.END)
        self.root.after(100, self.update_recv_display)

if __name__ == "__main__":
    root = tk.Tk()
    app = BroadcastApp(root)
    app.update_recv_display()  # 启动接收显示更新
    root.mainloop()
